package com.itheima.stock.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.itheima.stock.entity.*;
import com.itheima.stock.mapper.*;
import com.itheima.stock.properties.MarketProperties;
import com.itheima.stock.service.StockService;
import com.itheima.stock.util.DateTimeUtil;
import com.itheima.stock.vo.*;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author:gzc
 * @Date: 2025/10/23 16:04
 * @Description:
 */
@Service
public class StockServiceImpl implements StockService {


    private final StockBusinessMapper stockBusinessMapper;

    private final StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    private final MarketProperties marketProperties;
    private final StockMarketLogPriceMapper stockMarketLogPriceMapper;

    private final StockBlockRtInfoMapper stockBlockRtInfoMapper;

    private final StockRtInfoMapper stockRtInfoMapper;

    public StockServiceImpl(StockBusinessMapper stockBusinessMapper, StockMarketIndexInfoMapper stockMarketIndexInfoMapper, MarketProperties marketProperties, StockMarketLogPriceMapper stockMarketLogPriceMapper, StockBlockRtInfoMapper stockBlockRtInfoMapper, StockRtInfoMapper stockRtInfoMapper) {
        this.stockBusinessMapper = stockBusinessMapper;
        this.stockMarketIndexInfoMapper = stockMarketIndexInfoMapper;
        this.marketProperties = marketProperties;
        this.stockMarketLogPriceMapper = stockMarketLogPriceMapper;
        this.stockBlockRtInfoMapper = stockBlockRtInfoMapper;
        this.stockRtInfoMapper = stockRtInfoMapper;
    }

    @Override
    public List<StockBusiness> getAllStockBusiness() {
        return stockBusinessMapper.selectAll();
    }

    @Override
    public List<InnerMarketVO> innerIndexAll() {
        // 1. 获取国内大盘指数编码集合
        List<String> ids = marketProperties.getInner();
        // 2. 获取最近的有效的股票交易时间
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        // mock data TODO 模拟数据 用于测试
        String mockDate = "20211226105600";
        last = LocalDateTime.parse(mockDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        //TODO 模拟数据  以上两行仅用于测试


        // 3. 查询
        // 3.1 根据大盘编码和日期查询 股票大盘数据详情表
        List<StockMarketIndexInfo> marketIndexInfoList = stockMarketIndexInfoMapper.selectByIdsAndDateTime(ids, last);
        // 3.2 根据大盘编码和日期查询 股票大盘 开盘价与前收盘价流水表
        LocalDate localDate = last.toLocalDate();
        List<StockMarketLogPrice> marketLogPriceList = stockMarketLogPriceMapper.selectByIdsAndDateTime(ids, localDate);
        //4. 组装数据
        return marketIndexInfoList.stream().map((marketIndexInfo) -> {
                    InnerMarketVO vo = InnerMarketVO.builder()
                            .code(marketIndexInfo.getMarkId())
                            .name(marketIndexInfo.getMarkName())
                            .curDate(marketIndexInfo.getCurTime().toLocalDate().toString())
                            .tradeAmt(marketIndexInfo.getTradeAccount())
                            .tradeVol(marketIndexInfo.getTradeVolume())
                            .tradePrice(marketIndexInfo.getCurrentPrice())
                            .upDown(marketIndexInfo.getUpdownRate())
                            .build();
                    Optional<StockMarketLogPrice> matchedLogPrice = marketLogPriceList.stream()
                            .filter(marketLogPrice -> marketLogPrice.getMarketCode().equals(marketIndexInfo.getMarkId()))
                            .findFirst();

                    if (matchedLogPrice.isPresent()) {
                        StockMarketLogPrice logPrice = matchedLogPrice.get();
                        vo.setOpenPrice(logPrice.getOpenPrice());
                        vo.setPreClosePrice(logPrice.getPreClosePrice());
                    } else {
                        vo.setOpenPrice(BigDecimal.ZERO);
                        vo.setPreClosePrice(BigDecimal.ZERO);
                    }

                    return vo;
                }
        ).toList();
    }

    @Override
    public List<SectorAllVO> sectorAll() {

        //最近的一次股票交易时间
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //TODO 模拟数据
        last = LocalDateTime.parse("2021-12-21 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //模拟数据
        //获取当天的开盘时间
        LocalDateTime begin = DateTimeUtil.getOpenDate(last);
        //获取当天的收盘时间
        LocalDateTime end = DateTimeUtil.getCloseDate(last);
        List<StockBlockRtInfo> stockBlockRtInfoList = stockBlockRtInfoMapper.sectorAll(begin, end);
        List<SectorAllVO> list = stockBlockRtInfoList.stream().map(stockBlockRtInfo -> SectorAllVO.builder()
                .code(stockBlockRtInfo.getLabel())
                .name(stockBlockRtInfo.getBlockName())
                .companyNum(stockBlockRtInfo.getCompanyNum())
                .tradeAmt(stockBlockRtInfo.getTradeAmount())
                .tradeVol(stockBlockRtInfo.getTradeVolume())
                .avgPrice(stockBlockRtInfo.getAvgPrice())
                .curDate(stockBlockRtInfo.getCurTime().toLocalDate())
                .updownRate(stockBlockRtInfo.getUpdownRate()).build()).toList();
        return list;
    }

    @Override
    public List<IncreaseVO> increase() {
        //获取当前最近有效时间
        LocalDate last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now()).toLocalDate();
        //mock数据
        LocalDateTime currentTime = LocalDateTime.parse("2021-12-30 09:42:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<StockRtInfo> stockRtInfoList = stockRtInfoMapper.increase(currentTime);
        return stockRtInfoList.stream().map(stockRtInfo -> IncreaseVO.builder()
                .preClosePrice(stockRtInfo.getPreClosePrice())
                .tradeAmt(stockRtInfo.getTradeAmount())
                .code(stockRtInfo.getStockCode())
                .name(stockRtInfo.getStockName())
                .curDate(stockRtInfo.getCurTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
                .tradeVol(stockRtInfo.getTradeVolume())
                .tradePrice(stockRtInfo.getCurPrice())
                .amplitude((stockRtInfo.getMaxPrice().subtract(stockRtInfo.getMinPrice())).divide(stockRtInfo.getPreClosePrice(), 6, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(1.0)))
                .upDown(stockRtInfo.getCurPrice().subtract(stockRtInfo.getPreClosePrice()))
                .increase((stockRtInfo.getCurPrice().subtract(stockRtInfo.getPreClosePrice()).divide(stockRtInfo.getPreClosePrice(), 6, RoundingMode.HALF_UP)).multiply(BigDecimal.valueOf(1.0)))
                .build()).sorted((o1, o2) -> o2.getIncrease().compareTo(o1.getIncrease())).limit(10).toList();
    }

    @Override
    public StockAllVO<IncreaseVO> stockAll(Integer page, Integer pageSize) {
        PageHelper.<IncreaseVO>startPage(page, pageSize);
        Page<StockRtInfo> stockRtInfoList = stockRtInfoMapper.stockAll();
        List<IncreaseVO> increaseVOList = stockRtInfoList.stream().map(stockRtInfo -> IncreaseVO.builder()
                .preClosePrice(stockRtInfo.getPreClosePrice())
                .tradeAmt(stockRtInfo.getTradeAmount())
                .code(stockRtInfo.getStockCode())
                .name(stockRtInfo.getStockName().replaceAll("^\"|\"$", ""))
                .curDate(stockRtInfo.getCurTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
                .tradeVol(stockRtInfo.getTradeVolume())
                .tradePrice(stockRtInfo.getCurPrice())
                .amplitude((stockRtInfo.getMaxPrice().subtract(stockRtInfo.getMinPrice())).divide(stockRtInfo.getPreClosePrice(), 6, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(1.0)))
                .upDown(stockRtInfo.getCurPrice().subtract(stockRtInfo.getPreClosePrice()))
                .increase((stockRtInfo.getCurPrice().subtract(stockRtInfo.getPreClosePrice()).divide(stockRtInfo.getPreClosePrice(), 6, RoundingMode.HALF_UP)).multiply(BigDecimal.valueOf(1.0)))
                .build()).sorted((o1, o2) -> o2.getIncrease().compareTo(o1.getIncrease())).toList();

        return StockAllVO.<IncreaseVO>builder()
                .totalRows(stockRtInfoList.getTotal())
                .totalPages(stockRtInfoList.getPages())
                .pageNum(page)
                .pageSize(pageSize)
                .size(stockRtInfoList.size())
                .rows(increaseVOList)
                .build();
    }

    @Override
    public UpDownCountVO<CountVO> upDownCount() {
        //1.获取股票最近的有效交易日期,精确到秒
        LocalDateTime curDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //当前最近有效期
//        LocalDate curTime = curDateTime.toLocalDate();
//        //开盘日期
//        LocalDate openTime = DateTimeUtil.getOpenDate(curDateTime).toLocalDate();
        //TODO mock_data 后续数据实时获取时，注释掉
        LocalDateTime curTime = LocalDateTime.parse("2022-01-06 14:25:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime openTime = LocalDateTime.parse("2022-01-06 09:25:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //2.统计涨停的数据
        List<StockRtInfo> stockRtInfoList = stockRtInfoMapper.selectAllByDay(curTime, openTime);
        //计算涨跌停 大于等于0.1的为涨停 小于等于0.1的为跌停 按分钟分组
        Map<LocalDateTime, List<StockRtInfo>> collect = stockRtInfoList.stream().collect(Collectors.groupingBy(StockRtInfo::getCurTime));
        // 统计涨停数据（每个时间点一个统计对象）
        List<CountVO> upList = collect.entrySet().stream()
                .map(entry -> {
                    // 计算该时间点的涨停股票数量
                    long upCount = entry.getValue().stream()
                            .filter(stockRtInfo ->
                                    (stockRtInfo.getCurPrice().subtract(stockRtInfo.getPreClosePrice()))
                                            .divide(stockRtInfo.getPreClosePrice(), 6, RoundingMode.HALF_UP)
                                            .compareTo(new BigDecimal("0.1")) >= 0)
                            .count();
                    return CountVO.builder()
                            .time(entry.getKey().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))
                            .count(upCount)  // 该时间点的涨停数量
                            .build();
                }).toList();

        // 统计跌停数据
        List<CountVO> downList = collect.entrySet().stream()
                .map(entry -> {
                    // 计算该时间点的跌停股票数量
                    long downCount = entry.getValue().stream()
                            .filter(stockRtInfo ->
                                    (stockRtInfo.getCurPrice().subtract(stockRtInfo.getPreClosePrice()))
                                            .divide(stockRtInfo.getPreClosePrice(), 6, RoundingMode.HALF_UP)
                                            .compareTo(new BigDecimal("-0.1")) <= 0)
                            .count();

                    return CountVO.builder()
                            .time(entry.getKey().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))
                            .count(downCount)  // 该时间点的跌停数量
                            .build();
                }).toList();

        //5.响应
        return UpDownCountVO.<CountVO>builder()
                .upList(upList)
                .downList(downList).build();
    }

    @Override
    public TradeVolVO<CountVO> tradeVol() {
//        //1.获取最近的股票交易日时间，精确到分钟 T交易日
//        LocalDateTime tDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
//        //对应的开盘时间
//        LocalDateTime tOpenTime = DateTimeUtil.getOpenDate(tDateTime);
//        //对应的收盘时间
//        LocalDateTime tCloseTime = DateTimeUtil.getCloseDate(tDateTime);
        //TODO 后续注释掉 mock-data
        LocalDateTime tCloseTime = LocalDateTime.parse("2022-01-03 14:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //mock-data
        LocalDateTime tOpenTime = LocalDateTime.parse("2022-01-03 09:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //获取T-1交易日
//        LocalDateTime preTDateTime = DateTimeUtil.getPreviousTradingDay(tDateTime);
//        //对应的开盘时间
//        LocalDateTime preTOpenTime = DateTimeUtil.getOpenDate(preTDateTime);
//        //对应的收盘时间
//        LocalDateTime preTCloseTime = DateTimeUtil.getCloseDate(preTDateTime);
        //TODO 后续注释掉 mock-data
        LocalDateTime preTCloseTime = LocalDateTime.parse("2022-01-02 14:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //mock-data
        LocalDateTime preTOpenTime = LocalDateTime.parse("2022-01-02 09:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //2.获取T日和T-1日的股票大盘交易量统计数据
        List<StockMarketIndexInfo> stockMarketIndexInfoList = stockMarketIndexInfoMapper.stockTradeVolCount(marketProperties.getInner(), preTOpenTime, tCloseTime);
        //3.组装数据
        Map<LocalDateTime, List<StockMarketIndexInfo>> collect = stockMarketIndexInfoList.stream().collect(Collectors.groupingBy(StockMarketIndexInfo::getCurTime));
        //过滤出T日和T-1日的数据
        Map<LocalDateTime, List<StockMarketIndexInfo>> tCollect = collect.entrySet().stream()
                .filter(entry -> entry.getKey().isAfter(preTOpenTime) && entry.getKey().isBefore(preTCloseTime))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        Map<LocalDateTime, List<StockMarketIndexInfo>> preCollect = collect.entrySet().stream()
                .filter(entry -> entry.getKey().isAfter(tOpenTime) && entry.getKey().isBefore(tCloseTime))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        List<CountVO> voList = tCollect.entrySet().stream().map(entry -> {
            LocalDateTime key = entry.getKey();
            List<StockMarketIndexInfo> value = entry.getValue();
            long sum = value.stream().mapToLong(StockMarketIndexInfo::getTradeAccount).sum();
            return CountVO.builder()
                    .time(key.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))
                    .count(sum)
                    .build();
        }).toList();

        List<CountVO> yesVolList = preCollect.entrySet().stream().map(entry -> {
            LocalDateTime key = entry.getKey();
            List<StockMarketIndexInfo> value = entry.getValue();
            long sum = value.stream().mapToLong(StockMarketIndexInfo::getTradeAccount).sum();
            return CountVO.builder()
                    .time(key.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))
                    .count(sum)
                    .build();
        }).toList();

        return TradeVolVO.<CountVO>builder()
                .volList(voList)
                .yesVolList(yesVolList)
                .build();
    }

    @Override
    public UpDownVO<InfoVO> upDown() {
        //1.获取当前时间下最近的一个股票交易时间 精确到秒
        LocalDateTime avlDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //TODO 后续删除 mock-data
        avlDateTime = LocalDateTime.parse("2022-01-06 09:55:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //得到当前时间下所有个股的实时数据
        List<StockRtInfo> stockRtInfoList = stockRtInfoMapper.selectAllByLastTime(avlDateTime);
        //计算股票涨跌幅
        //统计各个涨跌区间的股票数量
        Map<String, Long> upDownStats = stockRtInfoList.stream().map(stock -> {
            return (stock.getCurPrice().subtract(stock.getPreClosePrice()))
                    .divide(stock.getPreClosePrice(), 4, RoundingMode.HALF_UP);
        }).collect(Collectors.groupingBy(
                this::getRateRange, // 根据涨跌幅获取区间标签
                Collectors.counting() // 统计数量
        ));

        // 构建InfoVO列表，确保所有区间都有值并按顺序排列
        List<InfoVO> infoList = buildOrderedInfoList(upDownStats);

        // 构建最终的响应对象
        return UpDownVO.<InfoVO>builder()
                .time(avlDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .infos(infoList)
                .build();
    }

    @Override
    public List<TimeSharingVO> timeSharing(String code) {
        //1.获取最近有效的股票交易时间
//        LocalDateTime curDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
//        //获取当前日期
//        Date curDate = curDateTime.toDate();
//        //获取当前日期对应的开盘日期
//        Date openDate = DateTimeUtil.getOpenDate(curDateTime).toDate();
        //TODO 后续删除 mock-data
        LocalDateTime openTime = LocalDateTime.parse("2022-01-06 09:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime closeTime = LocalDateTime.parse("2022-01-06 14:25:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<StockRtInfo> stockRtInfoList = stockRtInfoMapper.selectByCode(code, openTime, closeTime);
        return stockRtInfoList.stream().map(stock -> {
            return TimeSharingVO.builder()
                    .date(stock.getCurTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                    .code(stock.getStockCode())
                    .name(stock.getStockName())
                    .openPrice(stock.getOpenPrice())
                    .preClosePrice(stock.getPreClosePrice())
                    .tradePrice(stock.getCurPrice())
                    .highPrice(stock.getMaxPrice())
                    .lowPrice(stock.getMinPrice())
                    .tradeAmt(stock.getTradeAmount())
                    .tradeVol(stock.getTradeVolume())
                    .build();
        }).toList();
    }

    @Override
    public List<dkLineVO> dkLine(String code) {
        //获取当前日期前推20天
//        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
//        //当前时间节点
//        Date curTime = curDateTime.toDate();
//        //前推20
//        Date pre20Day = curDateTime.minusDays(20).toDate();

        //TODO 后续删除
        LocalDateTime curTime=LocalDateTime.parse("2022-01-06 14:25:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime pre20Day=LocalDateTime.parse("2022-01-01 09:30:00",  DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //获取指定范围的个股数据集合
        List<StockRtInfo> stockRtInfoList = stockRtInfoMapper.selectByCode(code, pre20Day, curTime);
        //按天分组
        Map<LocalDate, List<StockRtInfo>> collect = stockRtInfoList.stream().collect(Collectors.groupingBy(stockRtInfo -> stockRtInfo.getCurTime().toLocalDate()));
        //获取每天日期的最大值（收盘时间）对应的数据组装成dkLineVO
        List<StockRtInfo> list = collect.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .map(entry -> entry.getValue().stream()
                        .max(Comparator.comparing(StockRtInfo::getCurTime))
                        .orElse(null)).toList();

        return list.stream().map(stock -> {
            return dkLineVO.builder()
                    .date(stock.getCurTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                    .code(stock.getStockCode())
                    .name(stock.getStockName())
                    .openPrice(stock.getOpenPrice())
                    .preClosePrice(stock.getPreClosePrice())
                    .closePrice(stock.getCurPrice())
                    .highPrice(stock.getMaxPrice())
                    .lowPrice(stock.getMinPrice())
                    .tradeAmt(stock.getTradeAmount())
                    .tradeVol(stock.getTradeVolume()).build();
        }).toList();
    }

    /**
     * 根据涨跌幅获取对应的区间标签
     */
    private String getRateRange(BigDecimal rate) {
        if (rate.compareTo(BigDecimal.valueOf(0.07)) > 0) return ">7%";
        if (rate.compareTo(BigDecimal.valueOf(0.05)) > 0) return "5~7%";
        if (rate.compareTo(BigDecimal.valueOf(0.03)) > 0) return "3~5%";
        if (rate.compareTo(BigDecimal.ZERO) > 0) return "0~3%";
        if (rate.compareTo(BigDecimal.valueOf(-0.03)) > 0) return "-3~0%";
        if (rate.compareTo(BigDecimal.valueOf(-0.05)) > 0) return "-5~-3%";
        if (rate.compareTo(BigDecimal.valueOf(-0.07)) > 0) return "-7~-5%";
        return "<-7%";
    }

    /**
     * 构建有序的InfoVO列表，确保所有区间都有值且按正确顺序排列
     */
    private List<InfoVO> buildOrderedInfoList(Map<String, Long> stats) {
        // 定义所有区间及其显示顺序
        List<String> orderedRanges = Arrays.asList(
                "<-7%", "-7~-5%", "-5~-3%", "-3~0%", "0~3%", "3~5%", "5~7%", ">7%"
        );

        return orderedRanges.stream()
                .map(range -> InfoVO.builder()
                        .title(range)
                        .count(stats.getOrDefault(range, 0L))
                        .build()).collect(Collectors.toList());
    }
}

